/**
 *      Copyright (c) 2006-05-01 Yazy<yazy@mail.csdn.net>
 *      Licence see the licence file.
 */

#include "Pattern.hpp"
#include <sstream>
#include <iostream>
#include <cstdlib>

namespace yisc { namespace text { namespace pattern {

  String Literal::fetch(const String& target, Size s)
  {
    String result;
    if (Literal::match(target, s) == 1)
      result = target[s];
    return result;
  }

  Size Sequence::match(const String& target, Size s)
  {
    Size matched = 0;
    for (; matched < seq_.size() && s < target.size(); ++matched, ++s)
      if (seq_[matched] != target[s]) break;
    return matched;
  }

  /**
   * Syntax:
   *    range
   *            left-bracket spaces literal spaces dash spaces literal spaces right-bracket
   *    left-bracket
   *            [
   *    spaces
   *            (any amount of unprint-character: ' ')
   *    literal
   *            (any character except ']')
   *    dash
   *            -
   *    right-bracket
   *            ]
   *            
   * Example:
   *    [a-z]
   *    [0-9]
   */
  bool Range::parse(const String& modal, Size s)
  {
    bool result = false;
    Char low = 0, hight = 0;
    Primitive::length(0);
    if (modal.size() <= s) s = String::npos;
    else s = modal . find('[', s);
    
    if (String::npos != s)
      {
        ++s; while (' ' == modal[s]) ++s;
        low = modal[s];
        s = modal . find('-', s);
        if (String::npos != s)
          {
            ++s; while (' ' == modal[s]) ++s;
            hight = modal[s];
            if (String::npos != modal . find(']', s))
              result = true;
          }
      }
    
    if (result)
      {
        low_    = low;
        hight_  = hight;
        Primitive::length(1);
      }
    return result;
  }

  /**
   * Syntax:
   *    literal-repeater
   *            literal repeater
   *    repeater
   *            left-brace spaces number spaces right brace
   *    left-brace
   *            {
   *    number
   *            (any composition of "0123456789")
   *    right-brace
   *            }
   *            
   * Examples:
   *    a{10}   // repeat 'a' 10 times.
   *    
   *     {10}   // note that a space is in front of '{', repeat the space
   *            // 10 times.
   */
  bool RepeatLiteral::parse(const String& modal, Size s)
  {
    bool result = false;
    Char literal = 0;
    int repeater = 0;
    Primitive::length(0);

    if (modal.size() <= s) s = String::npos;
    else s = modal . find('{', s);
    
    if (String::npos != s && 0 < s) // at least one character must exist
                                    // before '{'.
      {
        literal = modal[s - 1]; // the retrived character is the one
                                // before '{'.
        Size numStart = s + 1;
        s = modal . find('}', s);
        if (String::npos != s)
          {
            std::istringstream ss(String(modal, numStart, s - numStart));
            if (ss>>repeater) result = true;
          }
      }
    
    if (result)
      {
        literal_        = literal;
        repeater_       = repeater;
        Primitive::length(repeater);
      }
    return result;
  }

  Size RepeatLiteral::match(const String& target, Size s)
  {
    Size matched = 0;
    for (; matched < repeater_ && s < target.size(); ++s, ++matched)
      if (literal_ != target[s]) break;
    return matched;
  }

  RepeatRange::~RepeatRange()
  {
    if (range_) delete range_;
  }

  /**
   * Syntax:
   *    range-repeater
   *            range repeater
   */
  bool RepeatRange::parse(const String& modal, Size s)
  {
    bool        result = false;
    Range       *range = 0;
    int         repeater = 0;
    Primitive::length(0);
    
    if (modal.size() <= s) s = String::npos;
    else s = modal . find('[', s);
    
    if (String::npos != s)
      {
        range = new Range(modal, s);
        if (0 == range->low() || 0 == range->hight())
          {
            delete range;
            range = 0;
          }
        else
          {
            Size surL = String::npos, surR = String::npos;
            surL = s = modal . find('{', s);
            surR = s = modal . find('}', s);
            if (String::npos != surL && String::npos != surR)
              {
                std::istringstream ss(String(modal, surL + 1, surR - surL));
                if (ss>>repeater) result = true;
              }
          }
      }

    if (result)
      {
        range_          = range;
        repeater_       = repeater;
        Primitive::length(repeater);
      }
    return result;
  }
  
  Size RepeatRange::match(const String& target, Size s)
  {
    Size matched = 0;
    if (0 != range_)
      {
        int repeated = 0, oneMatched;
        for (; repeated < repeater_ && s < target.size(); ++s, ++repeated)
          {
            oneMatched = range_->match(target, s);
            if (0 == oneMatched) break;
            matched += oneMatched;
          }
      }
    return matched;
  }

  /**
   * Syntax:
   *    
   */
  template<typename PrimitiveContainer, typename GroupContainer>
  static bool parsePrimitives(const String& modal, Size s, Size e,
                              PrimitiveContainer & pc, GroupContainer * gc)
  {
    bool result = false;

    if (String::npos == e) e = modal.size();
    if (modal.size() <= s) s = e = modal.size();
    
    Size i = s;
    Size position = s;
    
    for (; i < e; ++i)
      {
        // Range-repeater or Range
        if ('[' == modal[i])
          {
            Size leftBracket = i;
            Size rightBracket = modal.find(']', i + 1);
            if (String::npos == rightBracket)
              { result = false; break; }

            Size leftBrace = rightBracket + 1; //while(' ' == modal[i]) ++i;

            // Range Repeater
            if (leftBrace < e && '{' == modal[leftBrace])
              {
                Size rightBrace = modal.find('}', leftBrace + 1);
                if (String::npos == rightBrace)
                  { result = false; break; }
                
                RepeatRange * rr = new RepeatRange();
                if (rr->parse(modal, leftBracket) &&
                    0 != rr->range() && 0 <= rr->repeater())
                  {
//                     std::cout<<"range-repeater: "
//                              <<'['<<rr->range()->low()
//                              <<'-'<<rr->range()->hight()<<']'
//                              <<'{'<<rr->repeater()<<'}'
//                              <<std::endl;
                    pc.push_back(rr);
                    rr->start(position);
                    position += rr->length();
                  }
                else delete rr;
                
                i = rightBrace;
              }
            // Range
            else
              {
                Range * r = new Range();
                if (r->parse(modal, leftBracket) &&
                    0 != r->low() && 0 != r->hight())
                  {
//                     std::cout<<"range: "
//                              <<'['<<r->low()<<'-'<<r->hight()<<']'
//                              <<std::endl;
                    pc.push_back(r);
                    r->start(position);
                    position += r->length();
                  }
                else delete r;
                
                i = rightBracket;
              }
            continue;
          }

        // Literal Repeater
        if ('{' == modal[i])
          {
            if (s == i) { result = false; break; }

            Size rightBrace = modal.find('}', i + 1);
            
            RepeatLiteral * rl = new RepeatLiteral();
            if (rl->parse(modal, i - 1) && 
                0 != rl->literal() && 0 < rl->repeater())
              {
//                 std::cout<<"literal-repeater: "
//                          <<rl->literal()<<'{'<<rl->repeater()<<'}'
//                          <<std::endl;
                pc.push_back(rl);
                rl->start(position);
                position += rl->length();
              }
            else delete rl;
            
            i = rightBrace;
            continue;
          }

        // Group
        if ('(' == modal[i])
          {
            Size rightParan = modal . find(')', i + 1);//<<<<<
            if (String::npos == rightParan) { result = false; break; }
            
            Group * group = new Group();
            if (0 < group->parse(modal, i, rightParan))
              {
                //DEBUG_PATTERN(std::cout<<"group."<<std::endl);
                pc.push_back(group);
                group->start(position);
                position += group->length();
                
                if (gc) gc->push_back(group);
              }
            else delete group;
            
            i = rightParan;
            continue;
          }

        // Sequence and Literal
        {
          Size head = i;
          while (i < e &&
                 '(' != modal[i] &&
                 ')' != modal[i] &&
                 '[' != modal[i] &&
                 /*']' != modal[i] &&*/
                 '{' != modal[i] //&&
                 /*'}' != modal[i]*/) ++i;

          // do not eat the character belong to the Literal Repeater
          if ('{' == modal[i]) --i;
          if (head == i) continue;

          if (i - head == 1)
            {
              Literal * lit = new Literal(modal[head]);
              pc.push_back(lit);
              lit->start(position);
              position += lit->length();
              DEBUG_PATTERN(std::cout<<"literal: "<<lit->content()<<std::endl);
            }
          else 
            {
              Sequence * seq = new Sequence(modal, head, i);
              pc.push_back(seq);
              seq->start(position);
              position += seq->length();
              DEBUG_PATTERN(std::cout<<"sequence: "<<seq->content()<<std::endl);
            }

          --i; // push back one step to save '[', '(', '{' ...
               // because the "++i" expresion in the for(...)
               // will eat up them.
          continue;
        }
      }
    //DEBUG_PATTERN(std::cout<<"Compared: "<<i<<','<<e<<std::endl);
    result = (e <= i);
    return result;
  }
  template<typename PrimitiveContainer>
  static inline bool parsePrimitives(const String& modal, Size s, Size e,
                                     PrimitiveContainer & pc)
  { return parsePrimitives(modal, s, e, pc, (PrimitiveContainer*)0); }
  
  /**
   * Syntax:
   *    group
   *            (primitives)
   *    primitives
   *            any of
   *            sequence, range, literal-repeater, range-repeater, group
   */
  bool Group::parse(const String& modal, Size s, Size e)
  {
    bool result = false;
    
    if (0 == s) s = modal . find('(', s);
    clearPrimitives();
    Primitive::length(0);
    
    if (String::npos != s)
      result = parsePrimitives(modal, s + 1, e, primitives_, (Primitives*)0);

    if (result)
      Primitive::length(caculateLength());
    return result;
  }

  Size Group::match(const String& target, Size s)
  {
    Size matched = 0, oneMatched = 0;
    
    Primitives::iterator it = primitives_.begin();
    for (; it != primitives_.end(); ++it)
      {
        oneMatched = (*it)->match(target, s);
        //DEBUG_PATTERN(std::cout<<"Group::match: "<<oneMatched<<std::endl);
        if (oneMatched <= 0) break;
        matched += oneMatched;
        s       += oneMatched;
      }
    
    return matched;
  }

  Group::~Group()
  {
    clearPrimitives();
  }

  void Group::clearPrimitives()
  {
    Primitives::iterator it = primitives_.begin();
    for (; it != primitives_.end(); ++it)
      delete (*it);
  }

  Size Group::caculateLength() const
  {
    Size result = 0;
    Primitives::const_iterator it = primitives_.begin();
    for (; it != primitives_.end(); ++it)
      result += (*it)->length();
    return result;
  }
  
  /**
   * Syntax:
   *    pattern
   *            primitives
   */
  bool Pattern::parse(const String& modal, Size s, Size e)
  {
    bool result = false;
    
    clearPrimitives();
    
    if (String::npos != s)
      result = parsePrimitives(modal, s, e, primitives_, &groups_);

    if (result)
      Primitive::length(Group::caculateLength());
    
    return result;
  }

  int Pattern::compare(const String& lhs, const String& rhs) const
  {
    int result = -2;
    Groups::const_iterator it = compareGroups_.begin();
    const Char *s1, *s2;
    std::cout<<"----"<<std::endl;
    for (; it != compareGroups_.end(); ++it)
      {
        result = std::strncmp(lhs.c_str() + (*it)->start(),
                              rhs.c_str() + (*it)->start(),
                              (*it)->length());
//         std::cout<<std::string(lhs, (*it)->start(), (*it)->length())<<'='
//                  <<std::string(rhs, (*it)->start(), (*it)->length())
//                  <<','<<result<<std::endl;
        if (0 != result) break;
      }
    return result;
  }

}//namespace pattern
}//namespace text
}//namespace yisc

